package cn.com.yx.ms.service.impl;

import cn.com.yx.ms.domain.auth.LicenseUseConditionModel;
import cn.com.yx.ms.domain.auth.LicensesUseModel;
import cn.com.yx.ms.domain.auth.ServerInfoModel;
import cn.com.yx.ms.domain.dbmodel.*;
import cn.com.yx.ms.persistence.*;
import cn.com.yx.ms.service.ILicenseService;
import cn.yx.framework.configuration.ApiCode;
import cn.yx.framework.configuration.BaseConstant;
import cn.yx.framework.security.Des3Kit;
import cn.yx.framework.security.RSAKit;
import cn.yx.framework.util.DateUtil;
import cn.yx.framework.util.FileUtil;
import cn.yx.framework.util.ZipUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.security.PrivateKey;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * ApphubServiceImpl
 */
@Service
@Transactional
public class LicenseServiceImpl implements ILicenseService {

    private static String zippath = BaseConstant.getUploadConfig().getFilepath() + "/import/licensezip";
    private static String exportpath = BaseConstant.getUploadConfig().getFilepath() + "/export/licensezip";

    @Resource
    private AuthClientServerInfoMapper authClientServerInfoMapper;
    @Resource
    private AuthClientLicenseMapper authClientLicenseMapper;
    @Resource
    private AuthClientLicenseImportMapper authClientLicenseImportMapper;
    @Resource
    private AuthClientLicenseImportDetailMapper authClientLicenseImportDetailMapper;
    @Resource
    AuthClientLicenseDevMapMapper authClientLicenseDevMapMapper;
    @Resource
    private DevEquStoreMapper devEquStoreMapper;
    @Resource
    private RedisTemplate redisTemplate;

    // region << 公共 >>
    @Override
    public String getMaxNo(String tableName, String colName, String modelName) {
        String MaxNo = "";
        //拼接模块
        MaxNo += modelName;

        //获取当前时间
        SimpleDateFormat dateFormater = new SimpleDateFormat("yyyyMMdd");
        String date = dateFormater.format(new Date());
        MaxNo += date;

        //获取最大序列号
        String max = authClientLicenseMapper.getMaxOrderNo(tableName, colName, modelName, date);
        if (StringUtils.isNoneBlank(max)) {
            MaxNo += String.format("%03d", Integer.parseInt(max) + 1);
        } else {
            MaxNo += String.format("%03d", 1);
        }
        return MaxNo;
    }
    // endregion

    // region << 导入license压缩包 >>
    @Override
    public ApiCode importLicenseZip(MultipartHttpServletRequest request) throws Exception {

        // region << 导入压缩包逻辑说明 >>
        // 1.存入license的zip文件
        // 2.解压zip包至临时文件夹
        // 3.Des3Kit（对称）解密“key.ysb”
        // 4.用获取的prikey解密license文件内容
        // 5.校验每个license是否与服务器匹配
        // 6.存入"auth_client_importlicense"导入相关表
        // 7.新增或修改"auth_client_license"
        // endregion

        //声明导入逻辑返回码
        ApiCode apiCode = ApiCode.APP_100200;

        //接收客户端上传的压缩包并写入本地
        if (licenseZipWrite(request)) {

            // region << 声明逻辑处理需要的临时变量 >>

            // 压缩包原始文件名
            String zipName = request.getFile("file").getOriginalFilename();
            // 压缩包文件全路径
            String srcPath = zippath + "/" + zipName;
            // 解压目录
            String destPath = zippath + "/" + FileUtil.getFileName(zipName);
            // 压缩包下所有文件
            File[] files = null;
            // 压缩包下license文件
            File[] filelicenses = null;
            // 压缩包下key文件
            File filekey = null;
            // 解密后的私钥内容
            String priKey = "";
            // license加密字串集合
            List<String> enlicenseContent = null;
            // license解密字串集合
            List<String> delicenseContent = null;
            // 当前服务器信息实体
            ServerInfoModel clientServer = null;

            // endregion

            // region << 解压license压缩文件 >>
            ZipUtil.decompress(srcPath, destPath);
            // endregion

            // region << 获取解压目录下所有文件集合 >>
            files = findFiles(destPath);
            if (files.length > 0) {
                filelicenses = new File[files.length - 1];
                int j = 0;
                for (int i = 0; i < files.length; i++) {
                    if (files[i].getName().equals("key.ysb")) {
                        filekey = files[i];
                    } else {
                        filelicenses[j++] = files[i];
                    }
                }
            }
            // endregion

            // region << 解密license文件内容 >>

            enlicenseContent = new ArrayList<>();
            delicenseContent = new ArrayList<>();
            //解密key.ysb
            priKey = Des3Kit.decrypt(FileUtil.readFile(destPath + "/" + filekey.getName()));
            //解密License内容
            String content = "";
            PrivateKey privateKey = RSAKit.getPrivateKey(priKey);
            for (int i = 0; i < filelicenses.length; i++) {
                content = FileUtil.readFile(destPath + "/" + filelicenses[i].getName());
                enlicenseContent.add(content);
                String decodeLicenseContent = RSAKit.RSADecode(privateKey, Base64.decodeBase64(content));
                delicenseContent.add(decodeLicenseContent);
            }

            // endregion

            // region << 校验License文件内容 >>
            boolean ischeck = true;
            if (delicenseContent.size() > 0) {
                clientServer = authClientServerInfoMapper.findServerInfoModel();

                String[] splitContent = null;
                for (String item : delicenseContent) {
                    splitContent = item.split("\\|");
                    if (splitContent.length >= 4) {
                        if (splitContent[1].equals(clientServer.getCpuid()) && splitContent[2].equals(clientServer.getMac())) {
                            continue;
                        } else {
                            ischeck = false;
                            break;
                        }
                    }
                }
            }
            // endregion

            // region << 校验通过存库 >>
            if (ischeck) {
                if (enlicenseContent != null
                        && enlicenseContent.size() > 0
                        && enlicenseContent.size() == delicenseContent.size()) {
                    //获取现有License集合
                    List<AuthClientLicense> authClientLicenses = authClientLicenseMapper.findAll();

                    // region << 新增导入信息 >>
                    AuthClientLicenseImport authClientLicenseImport = new AuthClientLicenseImport();
                    authClientLicenseImport.setGuid(UUID.randomUUID().toString());
                    authClientLicenseImport.setNo(getMaxNo("auth_client_licenseimport", "no", "I"));
                    authClientLicenseImport.setCreatetime(new Date());
                    authClientLicenseImport.setDevnum(enlicenseContent.size());
                    authClientLicenseImportMapper.insert(authClientLicenseImport);
                    // endregion

                    for (int i = 0; i < enlicenseContent.size(); i++) {
                        String licid = delicenseContent.get(i).split("\\|")[0];
                        String endtime = delicenseContent.get(i).split("\\|")[3];

                        // region << 新增导入明细信息 >>
                        AuthClientLicenseImportDetail authClientLicenseImportDetail = new AuthClientLicenseImportDetail();
                        authClientLicenseImportDetail.setGuid(UUID.randomUUID().toString());
                        authClientLicenseImportDetail.setImportid(authClientLicenseImport.getGuid());
                        authClientLicenseImportDetail.setLicenseid(licid);
                        authClientLicenseImportDetail.setLicensecontent(enlicenseContent.get(i));
                        authClientLicenseImportDetail.setEndtime(DateUtil.string2Date(endtime, "yyyy-MM-dd"));
                        authClientLicenseImportDetail.setCreatetime(new Date());
                        authClientLicenseImportDetailMapper.insert(authClientLicenseImportDetail);
                        // endregion

                        //更新或创建license内容及授权截止日期
                        boolean isnew = true;
                        if (authClientLicenses != null && authClientLicenses.size() > 0) {
                            for (int j = 0; j < authClientLicenses.size(); j++) {
                                if (authClientLicenses.get(j).getGuid().equals(licid)) {
                                    isnew = false;
                                }
                            }
                        }

                        // region << license内容更新或创建 >>

                        AuthClientLicense authClientLicense = new AuthClientLicense();
                        authClientLicense.setGuid(licid);
                        authClientLicense.setLicenseid(licid);
                        authClientLicense.setLicensecontent(enlicenseContent.get(i));
                        authClientLicense.setEndtime(DateUtil.string2Date(endtime, "yyyy-MM-dd"));
                        authClientLicense.setLastupdatetime(new Date());

                        if (isnew) {
                            authClientLicense.setCreatetime(new Date());
                            authClientLicenseMapper.insert(authClientLicense);
                        } else {
                            authClientLicenseMapper.updateByPrimaryKey(authClientLicense);
                        }
                        // endregion
                    }
                }
            } else {
                apiCode = ApiCode.APP_221500;
            }
            // endregion

        }
        return apiCode;
    }

    /**
     * 压缩包写入本地
     *
     * @param request
     */
    private boolean licenseZipWrite(MultipartHttpServletRequest request) {
        boolean issucc = true;
        String fileSavePath = zippath;
        if (request != null) {
            try {
                String fileName = request.getFile("file").getOriginalFilename();
                FileUtil.writeFile(fileSavePath,
                        fileName,
                        request.getFile("file").getBytes());
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
                issucc = false;
            }
        }
        return issucc;
    }

    /**
     * 获取目录下所有文件
     *
     * @param path
     * @return
     */
    private File[] findFiles(String path) {
        File dir = new File(path);
        File[] files = dir.listFiles();
        return files;
    }
    // endregion

    // region << 使用License >>
    @Override
    public ApiCode UseLicense(List<LicensesUseModel> licensesUseModels) {
        ApiCode apiCode = ApiCode.APP_100200;

        // 校验需要使用的数量是否超出限制
        // 获取未绑定License的设备集合
        // 获取当前选择的授权截止日期下并且未关联设备的License集合
        // 筛选当前授权截止日期下的License集合
        // 建立License和设备关系
        //      判断未使用License和待激活设备数量关系，以数量小的为准

        boolean ischeck = true;
        List<LicenseUseConditionModel> licenseUseConditionModels = authClientServerInfoMapper.findLicUseConditionByServerId();
        if (licenseUseConditionModels != null && licenseUseConditionModels.size() > 0) {
            for (int i = 0; i < licenseUseConditionModels.size(); i++) {
                if (licensesUseModels != null && licensesUseModels.size() > 0) {
                    for (int j = 0; j < licensesUseModels.size(); j++) {
                        String formatDate = DateUtil.date2String(DateUtil.string2Date(licenseUseConditionModels.get(i).getEndtime(), "yyyy-MM-dd"), "yyyy-MM-dd");
                        if (licensesUseModels.get(j).getEndtime()
                                .equals(formatDate)) {
                            if (Integer.parseInt(licensesUseModels.get(j).getThisusecount())
                                    > licenseUseConditionModels.get(i).getUnusecount()) {
                                ischeck = false;
                            }
                        }
                    }
                }
            }
        }

        if (!ischeck) {
            apiCode = ApiCode.APP_221501;
            return apiCode;
        }


        List<DevEquStore> devEquStores = devEquStoreMapper.findUnActivedDev();
        List<AuthClientLicense> authClientLicenses = authClientLicenseMapper.findUnBindLicenses();
        List<AuthClientLicense> newAuthClientLicenses = new ArrayList<>();

        if (licensesUseModels != null && licensesUseModels.size() > 0) {
            for (int i = 0; i < licensesUseModels.size(); i++) {
                if (authClientLicenses != null && authClientLicenses.size() > 0) {
                    for (int j = 0; j < authClientLicenses.size(); j++) {

                        if (DateUtil.date2String(authClientLicenses.get(j).getEndtime(), "yyyy-MM-dd")
                                .equals(licensesUseModels.get(i).getEndtime())) {
                            newAuthClientLicenses.add(authClientLicenses.get(j));
                        }
                    }
                }
            }
        }

        int compare = 0;
        if (devEquStores.size() > newAuthClientLicenses.size()) {
            compare = -1;
        } else if (devEquStores.size() < newAuthClientLicenses.size()) {
            compare = 1;
        }

        AuthClientLicenseDevMap authClientLicenseDevMap = null;
        if (compare == 1) {
            for (int i = 0; i < devEquStores.size(); i++) {
                authClientLicenseDevMap = new AuthClientLicenseDevMap();
                authClientLicenseDevMap.setGuid(UUID.randomUUID().toString());
                authClientLicenseDevMap.setDevsn(devEquStores.get(i).getEquipmentno());
                authClientLicenseDevMap.setLicid(newAuthClientLicenses.get(i).getGuid());
                authClientLicenseDevMap.setCreatetime(new Date());
                authClientLicenseDevMap.setIsdeleted("0");
                authClientLicenseDevMapMapper.insert(authClientLicenseDevMap);
            }
        } else {
            for (int i = 0; i < newAuthClientLicenses.size(); i++) {
                authClientLicenseDevMap = new AuthClientLicenseDevMap();
                authClientLicenseDevMap.setGuid(UUID.randomUUID().toString());
                authClientLicenseDevMap.setLicid(newAuthClientLicenses.get(i).getGuid());
                authClientLicenseDevMap.setDevsn(devEquStores.get(i).getEquipmentno());
                authClientLicenseDevMap.setCreatetime(new Date());
                authClientLicenseDevMap.setIsdeleted("0");
                authClientLicenseDevMapMapper.insert(authClientLicenseDevMap);
            }
        }


        return apiCode;
    }


    // endregion

    // region << 更换设备编号 >>
    @Override
    public ApiCode changeLicenseDevInfo(String licid, String devsn) {
        ApiCode apiCode = ApiCode.APP_100200;

        // 删除License对应的dev关系（假删除）
        int setDeleteCount = authClientLicenseDevMapMapper.updateLicenseAndDevMap(licid);

        if (StringUtils.isNoneBlank(devsn)) {
            // 新增License和devsn关系
            AuthClientLicenseDevMap authClientLicenseDevMap = new AuthClientLicenseDevMap();
            authClientLicenseDevMap.setGuid(UUID.randomUUID().toString());
            authClientLicenseDevMap.setLicid(licid);
            authClientLicenseDevMap.setDevsn(devsn);
            authClientLicenseDevMap.setCreatetime(new Date());
            authClientLicenseDevMap.setIsdeleted("0");
            authClientLicenseDevMapMapper.insert(authClientLicenseDevMap);
        }

        return apiCode;
    }

    @Override
    public ApiCode clearLicenseDevInfo(String licid) {
        ApiCode apiCode = ApiCode.APP_100200;

        // 删除License对应的dev关系（假删除）
        int setDeleteCount = authClientLicenseDevMapMapper.updateLicenseAndDevMap(licid);

        return apiCode;
    }
    // endregion

    // region << 续费License导出 >>

    @Override
    public String exportLicenseZipInfo(List<AuthClientLicense> licids, HttpServletResponse res) throws Exception {


        String zipFileName = "client-export-";
        String[] licensefiles = null;

        if (licids != null && licids.size() > 0) {
            licensefiles = new String[licids.size()];
            for (int i = 0; i < licids.size(); i++) {
                //生成需要打包导出的License文件
                String filename = licids.get(i).getGuid() + ".ysb";
                FileUtil.writeFile(exportpath,
                        filename,
                        licids.get(i).getLicenseid().getBytes("UTF-8"));

                licensefiles[i] = exportpath + "/" + filename;
            }

            zipFileName += licensefiles.length + "-" + DateUtil.date2String(new Date(), "yyyy-MM-dd") + ".zip";
        }

        // region << 生成zip包 >>

        ZipUtil.writeZip(licensefiles, zipFileName, exportpath);

        // endregion

        //将导出的zip文件路径存入缓存
        String cacheId = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(cacheId, zipFileName, 300, TimeUnit.SECONDS);

        return cacheId;
        //导出
        //HttpExportFile(res, exportpath, zipFileName);
    }

    private void HttpExportFile(HttpServletResponse res, String path, String fileName) throws Exception {
        res.setHeader("content-type", "application/octet-stream");
        res.setContentType("application/octet-stream");
        res.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        byte[] buff = new byte[1024];
        BufferedInputStream bis = null;
        OutputStream os = null;

        os = res.getOutputStream();
        bis = new BufferedInputStream(new FileInputStream(new File(path + "/" + fileName)));
        int i = bis.read(buff);
        while (i != -1) {
            os.write(buff, 0, buff.length);
            os.flush();
            i = bis.read(buff);
        }

        if (bis != null) {
            bis.close();
        }

    }

    @Override
    public void downloadExportZipInfo(String cacheId, HttpServletResponse res) throws Exception {
        String zipFileName = (String) redisTemplate.opsForValue().get(cacheId);
        HttpExportFile(res, exportpath, zipFileName);
    }

    // endregion
}